package org.dromara.system.kernel;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.domain.*;
import org.dromara.system.mapper.*;
import org.dromara.system.mq.EmailSender;
import org.dromara.system.service.IEmailAccountService;
import org.dromara.system.service.IEmailListService;
import org.dromara.system.service.IEmailTemplateService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.SendEmailUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeMessage;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 发送邮件核心处理类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SendEmail2Kernel {

    private final EmailAlgorithmKernel emailAlgorithmKernel;
    private final IPayOrderService payOrderService;
    private final EmailAccountMapper emailAccountMapper;
    private final EmailListMapper emailListMapper;
    private final EmailTemplateMapper emailTemplateMapper;
    private final EmailConfigMapper emailConfigMapper;

    /**
     * 发送订单邮件
     *
     * @param orderId 订单ID
     */
    @Async
    public void sendOrderEmail(Long orderId) {
        PayOrder order = payOrderService.selectById(orderId);
        if (order == null) {
            return;
        }

        //构建一条邮件
        EmailList email = new EmailList();
        email.setType(1);
        email.setOrderId(order.getOrderId());
        email.setOrderNo(order.getOrderNo());
        email.setReceiveEmail(order.getCustomerEmail());
        email.setStatus(1);
        email.setIsDelete(0);
        email.setCreateId(order.getAgentId());
        email.setCreateName(order.getAgentName());

        //滚动邮箱发送邮件，直到发送成功，或者无可用账号
        loopSendEmail(order, email);
    }

    /**
     * 失败邮件重发
     *
     * @param emails 邮件列表
     * @return
     */
    @Async
    public void sendFailOrderEmail(List<EmailList> emails) {

        emails.forEach(email -> {
            PayOrder order = payOrderService.selectById(email.getOrderId());
            if (order == null) {
                return;
            }
            //滚动邮箱发送邮件，直到发送成功，或者无可用账号
            loopSendEmail(order, email);
        });
    }

    /**
     * 滚动邮箱发送邮件，直到发送成功，或者无可用账号
     *
     * @param order 订单
     * @param email 邮件
     */
    protected R<?> loopSendEmail(PayOrder order, EmailList email) {

        //根据收款账号查询发件配置
        EmailConfig config = emailConfigMapper.selectList(new LambdaQueryWrapper<EmailConfig>().like(EmailConfig::getPayAccountId, "," + order.getAccountId() + ",")).stream().findFirst().orElse(null);
        if (config == null) {
            return R.fail(updateEmailError(email, "未查询到支付邮件配置"));
        }
        if (Objects.equals(config.getStatus(), 0)) {
            return R.fail(updateEmailError(email, "支付邮件配置未启用"));
        }

        if (email.getEmailId() == null) {
            emailListMapper.insert(email);
        }

        if (StringUtils.isBlank(config.getEmailAccountId()) || config.getTemplateId() == null) {
            return R.fail(updateEmailError(email, "该收款账号对应的支付邮件配置有误"));
        }

        //查询邮件模板
        EmailTemplate template = emailTemplateMapper.selectById(config.getTemplateId());
        if (template == null) {
            return R.fail(updateEmailError(email, "未查询到邮件模板"));
        }

        //根据订单代理人查询发件邮箱信息
        Set<Long> emailAccountIds = Arrays.stream(config.getEmailAccountId().split(",")).filter(StringUtils::isNotBlank).map(Long::parseLong).collect(Collectors.toSet());
        List<EmailAccount> accounts = emailAccountMapper.selectBatchIds(emailAccountIds).stream().filter(account -> Objects.equals(account.getStatus(), 0)).collect(Collectors.toList());
        if (accounts.isEmpty()) {
            return R.fail(updateEmailError(email, "未查询到可用的发件邮箱"));
        }

        //滚动获取发件邮箱
        EmailAccount account = getLoopEmailAccount(order.getAgentId() + ":" + config.getConfigId(), accounts);
        if (account == null) {
            return R.fail(updateEmailError(email, "未轮询到可用发件邮箱"));
        }

        email.setTemplateId(template.getTemplateId());
        email.setSendEmailId(account.getId());
        email.setSendEmailAct(account.getAccount());
        emailListMapper.updateById(email);

        //设置邮件服务器配置
        SendEmailUtils.EmailServerCfg cfg = new SendEmailUtils.EmailServerCfg();
        cfg.setAccount(account.getAccount());
        cfg.setPassword(account.getPassword());
        cfg.setHost(account.getServer());
        cfg.setPort(account.getPort());
        cfg.setProtocol(account.getAgreement());
        cfg.setEnableSsl(account.getEnableSsl());

        //构建邮件服务session
        Session session = SendEmailUtils.getSession(cfg);

        // 4. 根据 Session 获取邮件传输对象
        Transport transport;
        try {
            transport = session.getTransport();
            transport.connect(account.getAccount(), account.getPassword());
        } catch (Exception e) {
            log.error("根据 Session 获取邮件传输对象异常", e);

            String msg = "获取邮件传输对象异常：";
            if (e.getMessage().contains("No provider for")) {
                msg = "邮件协议有误：";
            }

            if (e.getMessage().contains("Couldn't connect to host")) {
                msg = "邮件服务器地址有误：";
            }

            updateEmailError(email, msg + e.getMessage());

            //更新账号状态
            EmailAccount upAc = new EmailAccount();
            upAc.setId(account.getId());
            upAc.setStatus(1);
            emailAccountMapper.updateById(upAc);

            //递归，使用其他账号重试
            return loopSendEmail(order, email);
        }

        try {
            //处理邮件填充内容
            String content = template.getTemplate();
            String theme = template.getTheme();

            JSONObject params = buildDynoParams(template, order);
            if (!params.isEmpty()) {
                Set<String> keys = params.keySet();
                for (String key : keys) {
                    content = content.replace(key, params.getString(key));
                    theme = theme.replace(key, params.getString(key));
                }
            }

            //构建动态参数
            email.setDyncParam(params.toJSONString(JSONWriter.Feature.WriteMapNullValue));

            if (StringUtils.isNotEmpty(content)) {
                //构建邮件消息对象
                MimeMessage message = SendEmailUtils.buildMessage(cfg, session, email.getReceiveEmail(), theme, content);

                //发送邮件
                if (message != null) {
                    try {
                        // 6. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
                        transport.sendMessage(message, message.getAllRecipients());
                        email.setStatus(3);
                        email.setErrTime(null);
                        email.setErrContent("");
                        email.setSendTime(DateUtils.getNowDate());
                        email.setSendEmailId(account.getId());
                        email.setSendEmailAct(account.getAccount());

                        //更新订单邮件状态
                        PayOrder obj = new PayOrder();
                        obj.setOrderId(order.getOrderId());
                        if (Objects.equals(email.getType(), 1)) {
                            obj.setPayEmail(1);
                        } else if(Objects.equals(email.getType(), 2)) {
                            obj.setTrackEmail(1);
                        }
                        payOrderService.updatePayOrderAsync(obj);

                    } catch (MessagingException e) {
                        e.printStackTrace();
                        String msg = e.getMessage();
                        if (msg != null && msg.contains("Recipient address rejected: Email Sending Quota Exceeded")) {
                            msg = msg + "【收件人拒绝】";
                        }
                        email.setErrContent(msg);
                        email.setStatus(4);
                        email.setErrTime(DateUtils.getNowDate());
                    }
                }
            } else {
                email.setStatus(4);
                email.setErrTime(DateUtils.getNowDate());
                email.setErrContent("邮件内容为空，无法发送");
            }

            //更新邮件状态
            emailListMapper.updateById(email);

            // 7. 关闭连接
            transport.close();

            return R.ok();
        } catch (MessagingException e) {
            e.printStackTrace();
            email.setStatus(4);
            email.setErrContent("邮件处理异常" + e.getMessage());
            email.setErrTime(DateUtils.getNowDate());
            emailListMapper.updateById(email);
            return R.fail("邮件处理异常" + e.getMessage());
        }
    }

    /**
     * 轮询获取发件邮件账号
     *
     * @param agentId  代理ID
     * @param accounts 代理的邮件账号列表
     * @return 邮箱账号
     */
    private EmailAccount getLoopEmailAccount(String agentId, List<EmailAccount> accounts) {
        if (accounts.isEmpty()) {
            return null;
        }

        //已循环的邮件账号列表
        List<Long> loops = emailAlgorithmKernel.getLoopAccount(agentId);

        EmailAccount account = null;
        for (EmailAccount obj : accounts) {
            //循环收单，上次发件账号跳过
            if (!loops.isEmpty() && loops.contains(obj.getId())) {
                continue;
            }
            account = obj;
            break;
        }

        if (!loops.isEmpty() && account == null) {
            emailAlgorithmKernel.rmGroupLoop(agentId);
            return getLoopEmailAccount(agentId, accounts);
        }

        //记录本次发件账号
        if (account != null) {
            emailAlgorithmKernel.setLoopAccount(agentId, account.getId());
        }

        return account;
    }

    /**
     * 构建动态参数
     *
     * @param template 邮件模板
     * @param order    订单
     * @return 动态参数
     */
    public JSONObject buildDynoParams(EmailTemplate template, PayOrder order) {
        List<String> fields = extractFieldNames(template.getTemplate());

        //账单地址
        JSONObject customer = JSONObject.parseObject(order.getCustomer());
        //送货地址
        JSONObject shipping = JSONObject.parseObject(order.getShipping());
        //产品信息
        List<JSONObject> orderProduct = JSONArray.parseArray(order.getProduct(), JSONObject.class);

        JSONObject json = new JSONObject();

        for (String fieldName : fields) {
            // 使用反射获取PayOrder的字段值
            Object fieldValue = null;
            try {
                if (fieldName.startsWith("customer.")) {
                    if (customer != null) {
                        fieldValue = customer.get(fieldName.replace("customer.", ""));
                    }
                } else if (fieldName.startsWith("shipping.")) {
                    if (shipping != null) {
                        fieldValue = shipping.get(fieldName.replace("shipping.", ""));
                    }
                } else if (fieldName.equals("product.name")) {
                    if (orderProduct != null && !orderProduct.isEmpty()) {
                        fieldValue = orderProduct.stream().map(tmp -> tmp.getString("name")).collect(Collectors.joining("|"));
                    }
                } else {
                    Field field = PayOrder.class.getDeclaredField(fieldName);
                    field.setAccessible(true); // 如果字段是私有的，需要设置为可访问
                    fieldValue = field.get(order);
                }
            } catch (Exception ignored) {
            }
            json.put("{" + fieldName + "}", fieldValue == null ? "" : fieldValue);
        }
        // 使用Fastjson的toJSONString方法，并包含null值
        return json;
    }

    /**
     * 获取邮件主题和内容
     *
     * @param email    邮件
     * @param template 邮件模板
     * @param order    订单
     * @return 邮件主题和内容
     */
    public Pair<String, String> getEmailContent(EmailList email, EmailTemplate template, PayOrder order) {

        //处理邮件填充内容
        String content = template.getTemplate();
        String theme = template.getTheme();
        if (email.getType() == 2) {
            if (StringUtils.isNotEmpty(email.getDyncParam())) {
                JSONObject outParams = JSONObject.parseObject(email.getDyncParam());
                outParams.remove("B端订单号");

                JSONObject params = buildDynoParams(template, order);
                if (outParams.containsKey("物流单号")) {
                    params.put("{物流单号}", outParams.getString("物流单号"));
                }

                Set<String> keys = params.keySet();
                if (keys.size() > 0) {
                    for (String key : keys) {
                        content = content.replace(key, params.getString(key));
                        theme = theme.replace(key, params.getString(key));
                    }
                }
            }

            return Pair.of(theme, content);
        }

        if (email.getType() == 1) {
            JSONObject params = buildDynoParams(template, order);
            if (!params.isEmpty()) {
                Set<String> keys = params.keySet();
                if (keys.size() > 0) {
                    for (String key : keys) {
                        content = content.replace(key, params.getString(key));
                        theme = theme.replace(key, params.getString(key));
                    }
                }
            }
            return Pair.of(theme, content);
        }

        return Pair.of(theme, content);
    }

    /**
     * 获取动态字段名称列表
     *
     * @param content 邮件内容
     * @return 字段名称列表
     */
    public static List<String> extractFieldNames(String content) {
        List<String> fieldNames = new ArrayList<>();
        // 正则表达式匹配大括号中的任何内容
        Pattern pattern = Pattern.compile("\\{(\\w+(\\.\\w+)*)}");
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            // matcher.group(1) 获取括号内的内容  group(0) 整个匹配的部分
            fieldNames.add(matcher.group(1));
        }
        return fieldNames;
    }

    /**
     * 更新邮件发送失败
     *
     * @param email 邮件
     * @param msg   失败原因
     * @return 失败原因
     */
    public String updateEmailError(EmailList email, String msg) {
        try {
            if(email.getEmailId() != null) {
                EmailList update = new EmailList();
                update.setEmailId(email.getEmailId());
                update.setStatus(4);
                update.setErrContent(msg);
                update.setErrTime(DateUtils.getNowDate());
                emailListMapper.updateById(update);
            }
        }catch (Exception ignore){}
        return msg;
    }
}
